package io.github.bloquesoft.entity.storage.mybatisplus;

import lombok.extern.slf4j.Slf4j;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import tk.mybatis.mapper.common.Mapper;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class ClassMapperBeanRegisterImpl implements ClassMapperBeanRegister {

    private final Map<Type, Mapper<?>> mappers = new HashMap<>();

    @Override
    public void register(Mapper<?> mapper) {

        Result result = new Result();
        for (Type beanType : mapper.getClass().getGenericInterfaces()) {
            result.clear();
            findGenericInterfaceTypeRecursion(beanType, tk.mybatis.mapper.common.Mapper.class, result);
            if (result.isSuccess()) {
                if (mappers.containsKey(result.getPojoType())) {
                    log.error("Register Mybatis mapper error, beanType: " + beanType.getTypeName() + " exists.");
                } else {
                    mappers.put(result.getPojoType(), mapper);
                    log.info("Register Mybatis mapper success, bean: " + mapper.getClass().getName() + " beanType: " + beanType.getTypeName() + ", for pojo : " + result.getPojoType());
                }
            }
            /*if (genericInterface == null) {
                log.error("Could find generic Mapper interface, mapper class :" + mapper.getClass().getName());
                break;
            }
            if (genericInterface.getActualTypeArguments().length > 1){
                log.error("More than one entity in Mapper interface, could not distinguish witch is the entity, mapper class:" + mapper.getClass().getName());
                break;
            }
            Type entityType = genericInterface.getActualTypeArguments()[0];*/
            /*if (!mappers.containsKey(entityType))
            {
                mappers.put(entityType, mapper);
            }*/
        }
    }

    public Mapper<?> getMapper(Type entityType) {
        return this.mappers.get(entityType);
    }

    private ParameterizedTypeImpl findGenericInterfaceTypeRecursion(Type type, Class<?> matchedGenericInterface, Result result) {
        if (type == null) {
            return null;
        }

        Type[] genericInterfaces = null;
        if (type instanceof ParameterizedTypeImpl) {
            ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) type;
            if (parameterizedType.getActualTypeArguments().length > 0) {
                result.setPojoType(parameterizedType.getActualTypeArguments()[0]);
            }
            if (parameterizedType.getRawType().equals(matchedGenericInterface)) {
                result.success();
                return parameterizedType;
            }
            genericInterfaces = parameterizedType.getRawType().getGenericInterfaces();
        } else {
            if (type instanceof Class) {
                genericInterfaces = ((Class<?>) type).getGenericInterfaces();
            }
        }
        if (genericInterfaces != null) {
            for (Type interfaceType : genericInterfaces) {
                ParameterizedTypeImpl resultType = findGenericInterfaceTypeRecursion(interfaceType, matchedGenericInterface, result);
                if (resultType != null) {
                    return resultType;
                }
            }
        }
        return null;
    }

    private class Result {

        private Type pojoType;

        private Boolean success;

        public Result() {
            this.clear();
        }

        public Type getPojoType() {
            return this.pojoType;
        }

        public void setPojoType(Type pojoType) {
            if (this.pojoType == null) {
                this.pojoType = pojoType;
            }
        }

        public void success() {
            this.success = true;
        }

        public boolean isSuccess() {
            return this.success;
        }

        public void clear() {
            this.success = false;
            this.pojoType = null;
        }
    }
}
